package com.cliang.tools.verifycode;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Random;

/**
 * 生成图片流
 */
public class VerifyCodeUtils {
    public static final String VERIFY_CODES="23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
    private static Random random=new Random();

    /**
     * 使用系统默认字体生成验证码
     * @param verifyLength
     * @return
     */
    public static String generateVerifyCode(int verifyLength){
        return generateVerifyCode(verifyLength,VERIFY_CODES);
    }

    /**
     * 使用指定源生成验证码
     * @param verifyLength  验证码长度
     * @param source        验证码字符源
     * @return
     */
    public static String generateVerifyCode(int verifyLength,String source){
        if(source==null || source.length()==0){
            source=VERIFY_CODES;
        }
        int codesLen=source.length();
        Random random=new Random(System.currentTimeMillis());
        StringBuilder verifyCode=new StringBuilder(verifyLength);
        for(int i=0;i<verifyLength;i++){
            verifyCode.append(source.charAt(random.nextInt(codesLen-1)));
        }
        return verifyCode.toString();
    }

    /**
     * 生成指定验证码图像文件
     * @param width
     * @param height
     * @param outputFile
     * @param code
     * @throws IOException
     */
    public static void outputImage(int width,int height,File outputFile,String code) throws IOException{
        if(outputFile==null)
            return;
        File dir=outputFile.getParentFile();
        if(!dir.exists())
            dir.mkdirs();

        try {
            outputFile.createNewFile();
            FileOutputStream fileOutputStream=new FileOutputStream(outputFile);
            outputImage(width,height,fileOutputStream,code);
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成指定验证码图片流
     * @param width
     * @param height
     * @param outputStream
     * @param code
     * @throws IOException
     */
    public static void outputImage(int width,int height,OutputStream outputStream,String code) throws IOException{
        int verifyLength=code.length();
        BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        Random random=new Random();
        Graphics2D g2=image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        Color[] colors=new Color[5];
        Color[] colorSpace=new Color[] {Color.WHITE,Color.CYAN,Color.GRAY,Color.LIGHT_GRAY,Color.MAGENTA,Color.ORANGE,Color.PINK,Color.YELLOW};
        float[] fractions=new float[colors.length];
        for(int i=0;i<colors.length;i++){
            colors[i]=colorSpace[random.nextInt(colorSpace.length)];
            fractions[i]=random.nextFloat();
        }
        Arrays.sort(fractions);

        g2.setColor(Color.GRAY);    //设置边框颜色
        g2.fillRect(0,0,width,height);

        Color c=getRandColor(200,250);
        g2.setColor(c);             //设置背景色
        g2.fillRect(0,2,width,height-4);

        //绘制干扰线
        Random random1=new Random();
        g2.setColor(getRandColor(160,200)); //设置线条颜色
        for(int i=0;i<20;i++){
            int x=random1.nextInt(width-1);
            int y=random1.nextInt(height-1);
            int x1=random1.nextInt(6)+1;
            int y1=random1.nextInt(12)+1;
            g2.drawLine(x,y,x+x1+40,y+y1+20);
        }

        //添加噪点
        float yawRate=0.05f;    //噪声率
        int area=(int)(yawRate*width*height);
        for(int i=0;i<area;i++){
            int x=random1.nextInt(width);
            int y=random1.nextInt(height);
            int rgb=getRandomIntColor();
            image.setRGB(x,y,rgb);
        }

        shear(g2,width,height,c);   //使图片扭曲

        g2.setColor(getRandColor(100,160));
        int fontSize=height-4;
        Font font=new Font("Algerian",Font.ITALIC,fontSize);
        g2.setFont(font);

        char[] chars=code.toCharArray();
        for(int i=0;i<verifyLength;i++){
            AffineTransform affine=new AffineTransform();
            affine.setToRotation(Math.PI/4*random.nextDouble()*(random.nextBoolean()?1:-1),(width/verifyLength)*i+fontSize/2,height/2);
            g2.setTransform(affine);
            g2.drawChars(chars,i,1,((width-10)/verifyLength)*i+5,height/2+fontSize/2-10);
        }

        g2.dispose();
//        ImageIO.setUseCache(false); //不使用缓存
        ImageIO.write(image,"jpg",outputStream);

    }

    private static Color getRandColor(int fc,int bc){
        if(fc>255)
            fc=255;
        if(bc>255)
            bc=255;

        int r=fc+random.nextInt(bc-fc);
        int g=fc+random.nextInt(bc-fc);
        int b=fc+random.nextInt(bc-fc);
        return new Color(r,g,b);
    }

    private static int[] getRandomRgb(){
        int[] rgb=new int[3];
        for(int i=0;i<3;i++){
            rgb[i]=random.nextInt(255);
        }

        return rgb;
    }

    private static int getRandomIntColor(){
        int[] rgb=getRandomRgb();
        int color=0;
        for(int c:rgb){
            color=color << 8;
            color=color | c;
        }
        return color;
    }

    private static void shearX(Graphics graphics,int width,int height,Color color){
        int period=random.nextInt(2);
        boolean borderGap=true;
        int frames=1;
        int phase=random.nextInt(2);

        for(int i=0;i<height;i++){
            double d=(double) (period >> 1)*Math.sin((double)i/(double)period + (6.2831853071796862D * (double) phase)/(double)frames);
            graphics.copyArea(0,i,width,1,(int)d,0);
            if(borderGap){
                graphics.setColor(color);
                graphics.drawLine((int)d,i,0,i);
                graphics.drawLine((int)d+width,i,width,i);
            }
        }
    }

    public static void shearY(Graphics graphics,int width,int height,Color color){
        int period=random.nextInt(40)+10 ;
        boolean borderGap=true;
        int frames=20;
        int phase=7;

        for(int i=0;i<width;i++){
            double d=(period >> 1)*Math.sin((double) i/(double) period+(6.2831853071796862D * (double) phase)/(double)frames);
            graphics.copyArea(i,0,1,height,0,(int)d);
            if(borderGap){
                graphics.setColor(color);
                graphics.drawLine(i,(int)d,i,0);
                graphics.drawLine(i,(int)d+height,i,height);
            }
        }
    }

    private static void shear(Graphics graphics,int width,int height,Color color){
        shearX(graphics,width,height,color);
        shearY(graphics,width,height,color);
    }

    /**
     * 生成随机验证码文件，并返回验证码值
     * @param width
     * @param height
     * @param outputFile
     * @param verifyLength
     * @return
     */
    public static String outputVerifyImage(int width, int height, File outputFile,int verifyLength) throws IOException{
        String verifyCode=generateVerifyCode(verifyLength);
        outputImage(width,height,outputFile,verifyCode);
        return verifyCode;
    }

    /**
     *
     * @param width
     * @param height
     * @param outputStream
     * @param verifyLength
     * @return
     * @throws IOException
     */
    public static String outputVerifyImage(int width, int height, OutputStream outputStream,int verifyLength) throws IOException{
        String verifyCode=generateVerifyCode(verifyLength);
        outputImage(width,height,outputStream,verifyCode);
        return verifyCode;
    }
}
